package com.client;
//Fuck you thang Tri.
//asdlkfjalskdjfla;ksjdf;klj
//askdjflkasjdfkljsadf
//cho nguyen
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.*;

public class client {

	class Point {
		int x, y;

		Point(int a, int b) {
			x = a;
			y = b;
		}

		void set(int a, int b) {
			x = a;
			y = b;
		}

	}

	public static int inputInt(String s) {
		BufferedReader input = new BufferedReader(new InputStreamReader(
				System.in));
		System.out.print(s);
		int i = 0;
		// Integer.pa
		try {
			i = Integer.parseInt(input.readLine());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return i;
	}

	public static String inputStr(String s) {
		String aLine = "";
		BufferedReader input = new BufferedReader(new InputStreamReader(
				System.in));
		System.out.print(s);
		try {
			aLine = input.readLine();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return aLine;
	}

	/**
	 * @param args
	 */
	static String getServerIP() {
		return "192.168.1.103";
	}

	static int getServerPort() {
		return 9999;
	}

	static String getName() {
		return "DEMO" + String.valueOf((int) (10 * Math.random()));
	}

	private String server;
	private int port;
	private String name;
	private Socket sock;
	private BufferedReader in;
	private PrintWriter out;
	private Integer BOARD_SIZE;
	private Integer TIMEOUT;
        private Integer countTurn = 0;
        private boolean PLAYFIRST = false;
        public static final int ME = 1;
        public static final int OPPONENT = 1;
        private static Map currentBoardState = new Map();  // SIZE = 11
        

	client(String server, int port, String name) {
		this.server = server;
		this.port = port;
		this.name = name;
	}

	void connect() {
		// Create a socket with a timeout
		InetAddress addr = null;
		try {
			addr = InetAddress.getByName(this.server);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int port = this.port;

		// Create an unbound socket
		try {
			this.sock = new Socket(addr, port);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			this.in = new BufferedReader(new InputStreamReader(
					this.sock.getInputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// BufferedReader this.in = new BufferedReader(new
		// InputStreamReader(System.in));
		try {
			this.out = new PrintWriter(this.sock.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.printf("Connected to server (%s:%d)\n", this.server,
				this.port);
	}

	void login() throws IOException {

		String line;
		String name;
		do {
			name = inputStr("Enter name:");
			this.out.format("LOGIN %s\n", name);
			this.out.flush();
			line = this.in.readLine();
		} while (!line.matches("HELLO .*"));
		this.name = name;
		System.out.printf("My name is %s\n", this.name);
	}

	void getServerInfo() throws IOException {
		String line;
		do {
			line = this.in.readLine();
		} while (!line.matches("INFO [0-9]+ [0-9]+"));
		String[] arr = line.split(" ");
		this.BOARD_SIZE = Integer.valueOf(arr[1]);
		this.TIMEOUT = Integer.valueOf(arr[2]);
		System.out.printf("BOARD SIZE: %d x %d\nTIME OUT: %d seconds\n",
				this.BOARD_SIZE, this.BOARD_SIZE, this.TIMEOUT);
	}

	Point getPlayerMove() {
		String line;
		try {
			line = this.in.readLine();
			if (line.matches("MOVE [0-9]+ [0-9]+")) {
				String[] arr = line.split(" ");
				Point M = new Point(Integer.valueOf(arr[1]),
						Integer.valueOf(arr[2]));
				System.out.printf("Opponent move: (%d, %d)\n", M.x, M.y);
				return M;
			}

			// END GAME
			if (line.matches("GAME .*")) {
				String stt = line.split(" ")[1];
				System.out.printf("Game Status: %s\n", stt);
				System.exit(0);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

	Point getNextMove() {
		// TODO: CODE HERE
		// For this DEMO, We will get moves from keyboard
                if (PLAYFIRST) {
                    if (countTurn ==0) {
                        countTurn++;
                        return new Point(5, 5);
                    } 
                    else 
                    {
                        if (countTurn==1) {
                            countTurn++;
                            // chay ham minimax voi board 5x5
                            return new Point(3,3);
                        }
                    }
                }
                else {
                    if (countTurn ==0) {
                        countTurn++;
                        Random ranNum = new Random();
                        int nextInt = (ranNum.nextInt(4))%4;
                        if (nextInt==0) {
                            return new Point(4, 4);
                        }
                        if (nextInt==1) {
                            return new Point(4,6);
                        }
                        if (nextInt==2) {
                            return new Point(6,4);
                        }
                        if (nextInt==3) {
                            return new Point(6,6);
                        }
                    }                     
                }                
                return null;
                
                
                //xet truong hop phai danh trong 
                
               
		// System.out.print("Enter X, Y: ");
//		int X = 0, Y = 0;
//		Boolean flag = false;
//		while (!flag) {
//			String str = inputStr("Enter X, Y: ");
//			str = str.trim();
//			X = Integer.valueOf(str.split(" ")[0]);
//			Y = Integer.valueOf(str.split(" ")[1]);
//			return new Point(X, Y);
//		}	
	}
        
        ResultStruct resultArray[][] = new ResultStruct [11][11];
        ResultStruct[][] runHeristic() {                                    
            List<PointMap> succNewList = currentBoardState.findPointAvailMap(); 
            for (int j=0; j<succNewList.size();j++) {
                resultArray[succNewList.get(j).x][succNewList.get(j).y] = minimaxSearch(currentBoardState, succNewList.get(j),1, OPPONENT);
            }          
            return resultArray;
        }
	void sendMove(Point M) {
		this.out.format("MOVE %d %d\n", M.x+1, M.y+1);
		this.out.flush();
		String line;
		try {
			line = this.in.readLine();
			if (line.matches("OK MOVE")) {
				System.out.printf("Sent move (%d, %d) to server\n", M.x+1, M.y+1);
				return;
			}

			else {
				System.out.println("LOSE!");
				System.exit(0);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	void start() {
		do {
			// Get Opponent's move
                        System.out.println("Bat dau chuan bi nhan diem doi thu gui toi...");
			Point M2 = getPlayerMove();
                        Point opponent = new Point(M2.x-1, M2.y-1) ;
                        
//                        System.out.println("-Da nhan duoc diem M voi gia tri tu opponent: "+M.x+" "+M.y);
                        
			// Now, Use your AI knowledge to get the next move in TIMEOUT seconds
                        if (opponent.x ==-1 && opponent.y == -1) {                            
                            PLAYFIRST = true;
                        }
                        else currentBoardState.set(opponent.x,opponent.y,-1);
			Point M1 = getNextMove();
                        
                        
                        if (M1==null) {
                            if (resultArray[opponent.x][opponent.y].path.isEmpty()) {
                                System.out.println("sai raauf,,,,,,,,,,,,,,");
                            }
                            else {
                                int xCo = resultArray[opponent.x][opponent.y].path.peek().x;
                                int yCo = resultArray[opponent.x][opponent.y].path.peek().y;                            
                                M1 = new Point(xCo, yCo);
                            }
                        }
                        currentBoardState.set(M1.x,M1.y,1);
                       // System.out.println("-Minh quyet dinh danh diem M1: "+M1.x+" "+M1.y);
			// Send move to Server
			sendMove(M1);
                        runHeristic();

		} while (true);
	}
        
        private boolean player;
        private ResultStruct result;
        private static int depthLimit = 2;
        
        
        
        /**
         * ham kiem tra xem do sau tai vi tri da cho. 
         *      - neu lon hon hoac bang thi tra ve true: tuc dung tai day ko duyet them nua
         *      - neu nho hon thi tra ve false: tiep tuc sinh cay con
         * @param position
         * @param depth
         * @return 
         */
        static boolean deepEnough(Map position, int depth) {
            if (depth<depthLimit) {
                return true;
            }
            return false;
        }
        
        /**
         * ham tim luong gia tai thoi diem hien tai cua board voi vi tri moi dc them vao
         * dung ham set(int x, int y, int player)
         * @param position : ban co tai thoi dim hien tai
         * @param player: nguoi se di
         * @param insertedPosition : diem chon ma luot tiep theo minh hoac doi thu di
         * @return 
         */
        static int staticEvaluation(Map position, PointMap insertedPosition , int player) {
//            System.out.print("Player "+player+"______");
            return position.eval(insertedPosition.x, insertedPosition.y, player);
        }
        
        /**
         * ham tao ra cac buoc di tiep theo co the tu mot position hien tai
         * @param position
         * @param player
         * @return 
         */
        static List<PointMap> moveGenerate(Map position, int player){
            return position.findPointAvailMap();
        }
        
        
        /**
         * player:  -1: opponent
         *          1: me
         * Version without alphaBetacut
         * @param args
         * @throws IOException 
         */       
        static ResultStruct minimaxSearch (Map position,PointMap insertedPosition, int depth, int player){
            ResultStruct result = new ResultStruct();
//            for(int k=0; k<=depth;k++) {
//                    System.out.print("    ");
//                }
//            System.out.println("Thong so ham minimax: ");
//            for(int k=0; k<=depth;k++) {
//                    System.out.print("    ");
//                }
//            System.out.print("depth: "+depth+"    player: "+player+"   position: ");
            //position.printPointIsCheck();
//            System.out.println();
            
            if (!deepEnough(position, depth)) {                
                result.path= new Stack<PointMap>();
                result.path.push(insertedPosition);
//                for(int k=0; k<=depth;k++) {
//                    System.out.print("    ");
//                }
//                System.out.print("-VUOT DEPTH luong gia vi tri "+ insertedPosition.x+","+insertedPosition.y+":  ");
                result.value = staticEvaluation(position, insertedPosition, player);
//                System.out.println(result.value);
                return result;
            }
            //position.set(insertedPosition.x, insertedPosition.y, player);
            List<PointMap> successors = moveGenerate(position, player);
            
            if (successors.isEmpty()) {
                result.path= new Stack<PointMap>();
                result.path.push(insertedPosition);
//                for(int k=0; k<=depth;k++) {
//                    System.out.print("    ");
//                }
//                System.out.print("-KO GENERATE, luong gia vi tri "+ insertedPosition.x+","+insertedPosition.y+":  ");
                result.value = staticEvaluation(position, insertedPosition, player);
//                System.out.println(result.value);
                return result;
            }
            else {
                ResultStruct temp;
                int bestScore=0;
                Stack<PointMap> bestPath = new Stack<PointMap>();                
                for (int i=0; i<successors.size();i++) { 
//                    for(int k=0; k<=depth;k++) {
//                        System.out.print("    ");
//                    }
//                    System.out.println("    -Chay ham minimax cho diem "+successors.get(i).x+","+successors.get(i).y) ;               
                    position.set(successors.get(i).x, successors.get(i).y, player*-1);
                    temp = minimaxSearch(position, successors.get(i), depth+1 , player*(-1));
                    position.unSet(successors.get(i).x, successors.get(i).y);
//                    for(int k=0; k<=depth;k++) {
//                        System.out.print("    ");
//                    }
//                    System.out.println("    -Result: "+temp.value) ;
                    if (i==0) {
                        bestScore = -temp.value;
                        temp.path.push(insertedPosition);
                        bestPath = temp.path;   
                    }
                    int newValue = -(temp.value);
//                    for(int k=0; k<=depth;k++) {
//                        System.out.print("    ");
//                    }
                    //System.out.println("--i: "+i+"    new value: "+newValue+"     bestScore: "+bestScore);
                    if(newValue>bestScore) {
                        bestScore = newValue;
                        temp.path.push(successors.get(i));
                        bestPath = temp.path;                       
                    }                
                }
                result.path = bestPath;
                result.value = bestScore;
            }
            return result;            
        }

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		String str = getServerIP();
		int port = getServerPort();
		String name = getName();
                currentBoardState.init(11);
		client C = new client(str, port, name); 
		C.connect();
		C.login();
		C.getServerInfo();
		C.start();        
//                currentBoardState.set(1,1,1);
//                currentBoardState.set(2,1,-1);
//                currentBoardState.set(1,2,1);
                
//                ResultStruct abc = new ResultStruct();
//                System.out.println("Start running minimax ......");
//                currentBoardState.set(2, 2, -1);
//                abc = minimaxSearch(currentBoardState, new PointMap(2, 2), 0, -1);
//                System.out.println("End minimax.");
	}

}
